package com.dream.libxrec.camera;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.util.Size;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.luoye.bzyuvlib.BZYUVUtil;

import java.nio.ByteBuffer;
import java.util.Arrays;

public class CameraTest{

    protected Context mContext;
    protected String mCameraID = "0";//摄像头ID
    protected Size mCameraSize = null;
    private CameraDevice mCamera;
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
    private final HandlerThread mEncoderThread = new HandlerThread(
            "EncoderThread");
    private Handler mEncoderHandler;
    private ImageReader mImageReader = null;
    private CameraCaptureSession mCameraCaptureSession = null;
    private final BZYUVUtil mBzyuvUtil = new BZYUVUtil();//libyuv封装库

    public CameraTest(){}

    /**
     * 初始化
     * @param ct 上下文
     * @param cameraID 摄像头ID
     * @param size 分辨率
     */
    public void init(Context ct, String cameraID, Size size){
        //按照摄像头分辨率进行初始化
        this.mContext = ct;
        mImageReader =
                ImageReader.newInstance(size.getWidth(),
                        size.getHeight(),
                        ImageFormat.YUV_420_888, 2);
        this.mCameraID = cameraID;
        this.mCameraSize = size;
    }

    public void start(){
        initHandler();
        openCamera();
    }

    private void openCamera(){
        if (ActivityCompat.checkSelfPermission(mContext,
                Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){
            return;
        }

        CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);

        //初始化ImageReader，用于后续获取预览数据帧
        initImageReader();

        try {
            manager.openCamera(mCameraID, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    mCamera = camera;
                    startPreview();
                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {

                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {

                }
            }, mEncoderHandler);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initHandler() {
        mEncoderThread.start();
        mEncoderHandler = new Handler(mEncoderThread.getLooper());
    }

    private void initImageReader() {
        try {
            final int width = mCameraSize.getWidth();
            final int height = mCameraSize.getHeight();

            mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    synchronized (this){
                        Image image =  reader.acquireNextImage();

                        long start = System.nanoTime();
                        byte[] data =
                                mBzyuvUtil.preHandleYUV420(image,
                                        false,
                                        0);

                        if (mSurface != null&&mSurface.isValid()){
                            Canvas c = mSurface.lockHardwareCanvas();

                            BZYUVUtil.yuv420ToRGBA(data,mRgbaData,
                                    width,height,false,
                                    0);

                            mSurfaceBitmap.copyPixelsFromBuffer(ByteBuffer.wrap(mRgbaData));
                            c.drawBitmap(mSurfaceBitmap,mSurfaceMatrix,null);
                            mSurface.unlockCanvasAndPost(c);
                        }

                        Log.i("Test",
                                "单帧耗时("+width+"*"+height+")："+(System.nanoTime() - start)/1000000);
                        image.close();
                    }
                }
            }, mEncoderHandler);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private Surface mSurface = null;
    private Bitmap mSurfaceBitmap = null;
    private byte[] mRgbaData = null;
    private Matrix mSurfaceMatrix = null;

    public synchronized void setPreview(Surface surface) {

        if (surface == null){
            mSurface = null;
            this.mSurfaceBitmap.recycle();
            this.mSurfaceBitmap = null;
            this.mRgbaData = null;
            this.mSurfaceMatrix = null;
        }else{
            this.mSurfaceBitmap = Bitmap.createBitmap(mCameraSize.getWidth(),
                    mCameraSize.getHeight(),
                    Bitmap.Config.ARGB_8888);
            this.mRgbaData = new byte[mCameraSize.getWidth() * mCameraSize.getHeight() * 4];

            Canvas c = surface.lockHardwareCanvas();
            float scaleX =
                    (float) c.getWidth() / mSurfaceBitmap.getWidth();
            float scaleY =
                    (float) c.getHeight() / mSurfaceBitmap.getWidth();
            float scale = Math.min(scaleX, scaleY);
            float translateX = (c.getWidth() - mSurfaceBitmap.getWidth() * scale) / 2;
            float translateY = (c.getHeight() - mSurfaceBitmap.getWidth() * scale) / 2;
            Matrix matrix = new Matrix();
            matrix.setScale(scale, scale);
            matrix.postTranslate(translateX, translateY);
            this.mSurfaceMatrix = matrix;
            surface.unlockCanvasAndPost(c);

            mSurface = surface;
        }
    }

    private void startPreview() {
        try {
            mCamera.createCaptureSession(Arrays.asList(
                    mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    try {
                        CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                        captureRequestBuilder.addTarget(mImageReader.getSurface());
                        session.setRepeatingRequest(captureRequestBuilder.build(), null, mMainHandler);
                        mCameraCaptureSession = session;
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                }
            }, mEncoderHandler);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭摄像头
     */
    public void stop(){
        try {
            //停止预览，不再获取数据
            if (mCameraCaptureSession != null){
                mCameraCaptureSession.stopRepeating();
                mCameraCaptureSession = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            mCamera.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
